/**
 * Created with CLion.
 * Description: 
 * User: loong
 * Date: 2019-12-08
 * Time: 21:24
 */
#include "MyPcm.h"


/**
 * 功能：播放音频
 * @param default_wav_hdr 声音格式
 * @param fileName 文件名
 * @return 错误返回-1。正确返回0.
 */
//int MyPcm ::playBack(wave_pcm_hdr default_wav_hdr, const char *fileName) {
 int playBack(wave_pcm_hdr default_wav_hdr, const char *fileName) {
    //-----------------------playBack seting----------------------------------
    //PCI设备句柄
    snd_pcm_t *handle;
    int rec;
    int size = 0;

    //硬件信息和PCM流配置
    snd_pcm_hw_params_t *pcm_params;
    unsigned int val;
    int dir = 0;
    snd_pcm_uframes_t frames;

    int channels = default_wav_hdr.channels;
    int frequency = default_wav_hdr.samples_per_sec;
    int bit = default_wav_hdr.bits_per_sample;
    int datablock = default_wav_hdr.block_align;

    //1.打开设备，设置播放模式， SND_PCM_STREAM_PLAYBACK
    rec = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
    if (rec < 0) {
        perror("\nopen PCM device failed");
        return -1;
    }
    //2.1分配params结构体
    snd_pcm_hw_params_alloca(&pcm_params);
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params_alloca:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }
    //2.2初始化params
    rec = snd_pcm_hw_params_any(handle, pcm_params);
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params_any:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }
    //2.3设置访问类型
    rec = snd_pcm_hw_params_set_access(handle, pcm_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (rec < 0) {
        perror("\nsed_pcm_hw_set_access:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }

    //2.4设置采样格式
    switch (bit / 8) {
        case 1:
            snd_pcm_hw_params_set_format(handle, pcm_params, SND_PCM_FORMAT_U8);
            break;
        case 2:
            snd_pcm_hw_params_set_format(handle, pcm_params, SND_PCM_FORMAT_S16_LE);
            break;
        case 3:
            snd_pcm_hw_params_set_format(handle, pcm_params, SND_PCM_FORMAT_S24_LE);
            break;
        default:
            rec = -1;
            break;
    }
    if (rec < 0) {
        fprintf(stderr, "播放声音错误：音频采样位数参数错误！\n");
        snd_pcm_drain(handle);//播放挂起的帧后停止PCM设备
        snd_pcm_close(handle);//关闭PCM句柄
        return -1;
    }

    //2.5设置为通道数
    rec = snd_pcm_hw_params_set_channels(handle, pcm_params, channels); //设置声道,1表示单声道，2表示立体声
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params_set_channels:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }
    //2.6设置采样率
    val = frequency;
    rec = snd_pcm_hw_params_set_rate_near(handle, pcm_params, &val, &dir); //设置频率
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params_set_rate_near:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }
    //3.将硬件参数设置应用于PCM设备并准备设备
    rec = snd_pcm_hw_params(handle, pcm_params);
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params: ");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }

    //4.获取周期长度
    rec = snd_pcm_hw_params_get_period_size(pcm_params, &frames, &dir);
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params_get_period_size:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }

    size = frames * datablock; /*4 代表数据块长度*/
    printf("%s\n", "声音播放设置完毕");
    //------------------------------------playback---------------------------------------
    char *buffer;
    int ret;
    buffer = (char *) malloc(size);
    FILE *fp;
    fp = fopen(fileName, "rb");
    if (fp == NULL) {
        perror("open file failed:\n");
        exit(1);
    }
    fseek(fp, 44, SEEK_SET); //定位歌曲到数据区

    while (1) {
        memset(buffer, 0, sizeof(buffer));
        ret = fread(buffer, 1, size, fp);
        if (ret == 0) {
            printf("歌曲写入结束\n");
            break;
        } else if (ret != size) {
        }
        // 写音频数据到PCM设备
        while (ret = snd_pcm_writei(handle, buffer, frames) < 0) {
            usleep(2000);
            if (ret == -EPIPE) {
                /* EPIPE means underrun */
                fprintf(stderr, "underrun occurred\n");
                //完成硬件参数设置，使设备准备好
                snd_pcm_prepare(handle);
            } else if (ret < 0) {
                fprintf(stderr, "error from writei: %s\n",
                        snd_strerror(ret));
            }
        }

    }
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
    return 0;
}

/**
 * 功能：捕捉声音
 * @param default_wav_hdr 声音格式
 * @param fileName 文件名
 * @return 错误返回-1。正确返回0.
 */
//int MyPcm ::capture(wave_pcm_hdr default_wav_hdr, const char *fileName) {
int capture(wave_pcm_hdr default_wav_hdr, const char *fileName) {
    //-----------------------------------capture setting-------------------------
    long loops;
    int rec;
    int size;
    //PCI设备句柄
    snd_pcm_t *handle;
    //硬件信息和PCM流配置
    snd_pcm_hw_params_t *pcm_params;
    unsigned int val;
    int dir;

    snd_pcm_uframes_t frames;
    char *buffer;

    wave_pcm_hdr wavheader = default_wav_hdr;
    int total_len = 0;
    FILE *fd = fopen(fileName, "wb");
    if (fd == NULL) {
        perror("文件打开失敗\n");
        return 0;
    }
    //添加wav音频头，使用采样率为16000
    fwrite(&wavheader, 1, sizeof(wave_pcm_hdr), fd);

    //1.打开设备，设置播放模式， SND_PCM_STREAM_CAPTURE
    rec = snd_pcm_open(&handle, "hw:1,0", SND_PCM_STREAM_CAPTURE, 0);
    if (rec < 0) {
        perror("unable to open pcm device: %s\n");
        return  -1;
    }

    //2.1分配params结构体
    snd_pcm_hw_params_alloca(&pcm_params);
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params_alloca:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }

    //2.2初始化params
    rec = snd_pcm_hw_params_any(handle, pcm_params);
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params_any:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }

    /* Set the desired hardware parameters. */

    //2.3设置访问类型
    rec = snd_pcm_hw_params_set_access(handle, pcm_params, SND_PCM_ACCESS_RW_INTERLEAVED);
    if (rec < 0) {
        printf("\nsed_pcm_hw_set_access:%d",rec);
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }

    //2.4设置采样格式
    rec = snd_pcm_hw_params_set_format(handle, pcm_params, SND_PCM_FORMAT_S16_LE);
    if (rec < 0) {
        fprintf(stderr, "音频采样位数参数错误！\n");
        snd_pcm_drain(handle);//播放挂起的帧后停止PCM设备
        snd_pcm_close(handle);//关闭PCM句柄
        return -1;
    }

    //2.5设置为通道数
    rec = snd_pcm_hw_params_set_channels(handle, pcm_params, 1);
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params_set_channels:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }

    /* 44100 bits/second sampling rate (CD quality) */
    val = 16000;
    snd_pcm_hw_params_set_rate_near(handle, pcm_params, &val, &dir);
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params_set_rate_near:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }

    /* Set period size to 32 frames. */
    frames = 32;
    snd_pcm_hw_params_set_period_size_near(handle, pcm_params, &frames, &dir);

    //3.将硬件参数设置应用于PCM设备并准备设备
    rec = snd_pcm_hw_params(handle, pcm_params);
    if (rec < 0) {
        printf("\nsnd_pcm_hw_params:%d ",rec);
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }

    //4.获取周期长度
    rec = snd_pcm_hw_params_get_period_size(pcm_params, &frames, &dir);
    if (rec < 0) {
        perror("\nsnd_pcm_hw_params_get_period_size:");
        snd_pcm_drain(handle);
        snd_pcm_close(handle);
        return -1;
    }
    size = frames * 2; /* 2 bytes/sample, 2 channels */
    buffer = (char *) malloc(size);
    printf("%s\n", "声音捕捉设置完毕");

    //5.获取周期时间长度
    snd_pcm_hw_params_get_period_time(pcm_params, &val, &dir);
    loops = 5000000/val;
    //实际录制时间为约等于 loops*2秒
    printf("loops%d\n", loops);

    //while (1) {
    while (loops -- >0) {
        //if (flag){
            rec = snd_pcm_readi(handle, buffer, frames);
            if (rec == -EPIPE) {
                /* EPIPE means overrun */
                fprintf(stderr, "overrun occurred\n");
                snd_pcm_prepare(handle);
            } else if (rec < 0) {
                fprintf(stderr, "error from read: %s\n", snd_strerror(rec));
            } else if (rec != (int) frames) {
                fprintf(stderr, "short read, read %d frames\n", rec);
            }
            rec = fwrite(buffer, 1, size, fd);
            total_len += size;
            printf("loops:%d#\n",loops);
            if (rec != size)
                fprintf(stderr, "short write: wrote %d bytes\n", rec);
        //} else{
        //    break;
        //}
    }
    /* 修正wav文件头数据的大小 */
    unsigned long temp = total_len + sizeof(wave_pcm_hdr) - 8;
    wavheader.size_8 = temp;
    /* 将修正过的数据写回文件头部,音频文件为wav格式 */
    wavheader.data_size =total_len;
    fseek(fd, 0, SEEK_SET);
    fwrite(&wavheader, 1, sizeof(wave_pcm_hdr), fd);

    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
    fclose(fd);
    return 0;
}

//bool MyPcm::isFlag() const {
//    return flag;
//}
//
//void MyPcm::setFlag(bool flag) {
//    MyPcm::flag = flag;
//}


